﻿using CLROBS;
using Cyotek.Windows.Forms;
using DesertKharonIrcPlugin.Emotes;
using DesertKharonIrcPlugin.Emotes.Twitch;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace DesertKharonIrcPlugin
{
    public partial class ConfigurationDialog : Form
    {
        private XElement _config;
        private DesertKharonIrcPluginSettings _settings;

        private IrcManager _ircManager;

        private bool _differentBroadcasterNickName = false;
        private bool _differentChannelName = false;

        public ConfigurationDialog(XElement config)
        {
            InitializeComponent();

            _config = config;
            _settings = new DesertKharonIrcPluginSettings(config);
        }

        private void LoadConfig()
        {
            // IRC log in
            presetComboBox.SelectedIndex = (int)_settings.LoginPreset; // TODO: Will crash if the config gets edited wrong by hand ... 
            serverTextBox.Text = _settings.IrcServer;
            portTextBox.Text = _settings.IrcServerPort.ToString();
            UpdateConfigByLoginPreset();

            nickNameTextBox.Text = _settings.IrcServerNickName;
            passTextBox.Text = _settings.IrcServerPassword;
            broadcasterNickNameTextBox.Text = _settings.BroadcasterNickName;
            channelNameTextBox.Text = _settings.IrcServerChannelName;
            
            // General
            //resolutionWidthTextBox.Text = _settings.RenderWidth.ToString();
            //resolutionHeightTextBox.Text = _settings.RenderHeight.ToString();
            alignmentHorizontalRadioButton.Checked = _settings.RenderAlignment == RenderAlignment.Horizontal;
            alignmentVerticalRadioButton.Checked = _settings.RenderAlignment == RenderAlignment.Vertical;
            fontDialog.Font = _settings.Font;
            fontColorViewLabel.BackColor = _settings.FontColor;
            outlineCheckBox.Checked = _settings.FontOutline;
            outlineThicknessComboBox.SelectedIndex = _settings.FontOutlineThickness - 1;
            outlineColorViewLabel.BackColor = _settings.FontOutlineColor;
            backgroundColorViewLabel.BackColor = _settings.BackgroundColor;
            nickNameFormatTextBox.Text = _settings.NickNameFormat;
            followupNickNameFormatTextBox.Text = _settings.FollowupNickNameFormat;
            twitchColorsCheckBox.Checked = _settings.TwitchColors;
            messageFormatTextBox.Text = _settings.MessageFormat;
            timeFormatTextBox.Text = _settings.TimeFormat;
            showPartsJoinsCheckBox.Checked = _settings.ShowJoinsParts;
            parseStandardMacrosCheckBox.Checked = _settings.ParseTwitchEmotes;
            //parseSubscriberMacrosCheckBox.Checked = _settings.ParseSubscriberMacros;
            //parseBTTVMacrosCheckBox.Checked = _settings.ParseBTTVMacros;

            // I don't like this code either :P ...
            float fadeOutTime = _settings.MessageFadeOutTimeInSeconds;
            var selectedFadeOutTimeElement = messageFadeOutTimeComboBox.Items.Cast<string>()
                .Select(i => new { Index = messageFadeOutTimeComboBox.Items.IndexOf(i), Value = TimeTextToSeconds((string)i) })
                .Where(e => e.Value <= fadeOutTime + 0.001f)
                .OrderByDescending(e => e.Value)
                .FirstOrDefault();

            messageFadeOutTimeComboBox.SelectedIndex = selectedFadeOutTimeElement != null ? selectedFadeOutTimeElement.Index : 0;

            // I don't like this code either :P ...
            float fadeOutTimeAfter = _settings.MessageFadeOutAfterInSeconds;
            var selectedFadeOutTimeAfterElement = fadeOutMessageAfterComboBox.Items.Cast<string>()
                .Select(i => new { Index = fadeOutMessageAfterComboBox.Items.IndexOf(i), Value = TimeTextToSeconds((string)i) })
                .Where(e => e.Value <= fadeOutTimeAfter + 0.001f)
                .OrderByDescending(e => e.Value)
                .FirstOrDefault();

            fadeOutMessageAfterComboBox.SelectedIndex = selectedFadeOutTimeAfterElement != null ? selectedFadeOutTimeAfterElement.Index : 0;

            // Yes, again, ugly code, woohoo ...
            float opacity = _settings.Opacity;
            var selectedOpacityElement = opacityComboBox.Items.Cast<string>()
                .Select(i => new { Index = opacityComboBox.Items.IndexOf(i), Value = Convert.ToSingle(i) / 100f })
                .Where(e => e.Value <= opacity + 0.001f)
                .OrderByDescending(e => e.Value)
                .FirstOrDefault();

            opacityComboBox.SelectedIndex = selectedOpacityElement != null ? selectedOpacityElement.Index : 0;


            // Colors
            // Broadcaster
            broadcasterNicknameColorViewLabel.BackColor = _settings.BroadcasterNicknameFontColor;
            broadcasterNicknameOutlineColorCheckBox.Checked = _settings.DifferentBroadcasterNicknameOutline;
            broadcasterNicknameOutlineColorViewLabel.BackColor = _settings.BroadcasterNicknameFontOutlineColor;
            broadcasterMessageColorViewLabel.BackColor = _settings.BroadcasterMessageFontColor;
            broadcasterMessageOutlineColorCheckBox.Checked = _settings.DifferentBroadcasterMessageOutline;
            broadcasterMessageOutlineColorViewLabel.BackColor = _settings.BroadcasterMessageFontOutlineColor;

            // Moderator
            moderatorNicknameColorViewLabel.BackColor = _settings.ModeratorNicknameFontColor;
            moderatorNicknameOutlineColorCheckBox.Checked = _settings.DifferentModeratorNicknameOutline;
            moderatorNicknameOutlineColorViewLabel.BackColor = _settings.ModeratorNicknameFontOutlineColor;
            moderatorMessageColorViewLabel.BackColor = _settings.ModeratorMessageFontColor;
            moderatorMessageOutlineColorCheckBox.Checked = _settings.DifferentModeratorMessageOutline;
            moderatorMessageOutlineColorViewLabel.BackColor = _settings.ModeratorMessageFontOutlineColor;

            // Friend
            friendNicknameColorViewLabel.BackColor = _settings.FriendNicknameFontColor;
            friendNicknameOutlineColorCheckBox.Checked = _settings.DifferentFriendNicknameOutline;
            friendNicknameOutlineColorViewLabel.BackColor = _settings.FriendNicknameFontOutlineColor;
            friendMessageColorViewLabel.BackColor = _settings.FriendMessageFontColor;
            friendMessageOutlineColorCheckBox.Checked = _settings.DifferentFriendMessageOutline;
            friendMessageOutlineColorViewLabel.BackColor = _settings.FriendMessageFontOutlineColor;

            // Foe
            foeNicknameColorViewLabel.BackColor = _settings.FoeNicknameFontColor;
            foeNicknameOutlineColorCheckBox.Checked = _settings.DifferentFoeNicknameOutline;
            foeNicknameOutlineColorViewLabel.BackColor = _settings.FoeNicknameFontOutlineColor;
            foeMessageColorViewLabel.BackColor = _settings.FoeMessageFontColor;
            foeMessageOutlineColorCheckBox.Checked = _settings.DifferentFoeMessageOutline;
            foeMessageOutlineColorViewLabel.BackColor = _settings.FoeMessageFontOutlineColor;

            // User
            userNicknameColorViewLabel.BackColor = _settings.UserNicknameFontColor;
            userNicknameOutlineColorCheckBox.Checked = _settings.DifferentUserNicknameOutline;
            userNicknameOutlineColorViewLabel.BackColor = _settings.UserNicknameFontOutlineColor;
            userMessageColorViewLabel.BackColor = _settings.UserMessageFontColor;
            userMessageOutlineColorCheckBox.Checked = _settings.DifferentUserMessageOutline;
            userMessageOutlineColorViewLabel.BackColor = _settings.UserMessageFontOutlineColor;

            // Lists
            if (_settings.ModeratorList != null)
                _settings.ModeratorList.ForEach(i => moderatorListView.Items.Add(new ListViewItem(i)));

            if (_settings.FriendList != null)
                _settings.FriendList.ForEach(i => friendListView.Items.Add(new ListViewItem(i)));

            if (_settings.FoeList != null)
                _settings.FoeList.ForEach(i => foeListView.Items.Add(new ListViewItem(i)));

            if (_settings.IgnoreList != null)
                _settings.IgnoreList.ForEach(i => ignoreListView.Items.Add(new ListViewItem(i)));

            UpdateFontDisplay();
            UpdateOutlineControllsStateInOrder();
        }

        private void SaveConfig()
        {
            // IRC log in
            _settings.LoginPreset = (LoginPreset)presetComboBox.SelectedIndex;
            _settings.IrcServerNickName = nickNameTextBox.Text;
            _settings.IrcServerPassword = passTextBox.Text;
            _settings.IrcServer = serverTextBox.Text;
            _settings.IrcServerPort = ToInt(portTextBox.Text);
            _settings.BroadcasterNickName = broadcasterNickNameTextBox.Text;
            _settings.IrcServerChannelName = channelNameTextBox.Text;

            // General
            _settings.RenderAlignment = alignmentHorizontalRadioButton.Checked ? RenderAlignment.Horizontal : RenderAlignment.Vertical;
            _settings.Font = fontDialog.Font;
            _settings.FontColor = fontColorViewLabel.BackColor;
            _settings.FontOutline = outlineCheckBox.Checked;
            _settings.FontOutlineThickness = outlineThicknessComboBox.SelectedIndex + 1;
            _settings.FontOutlineColor = outlineColorViewLabel.BackColor;
            _settings.BackgroundColor = backgroundColorViewLabel.BackColor;
            _settings.NickNameFormat = nickNameFormatTextBox.Text;
            _settings.TwitchColors = twitchColorsCheckBox.Checked;
            _settings.MessageFormat = messageFormatTextBox.Text;
            _settings.FollowupNickNameFormat = followupNickNameFormatTextBox.Text;
            _settings.TimeFormat = timeFormatTextBox.Text;
            _settings.MessageFadeOutAfterInSeconds = TimeTextToSeconds(fadeOutMessageAfterComboBox.SelectedItem as string);
            _settings.MessageFadeOutTimeInSeconds = TimeTextToSeconds(messageFadeOutTimeComboBox.SelectedItem as string);
            _settings.Opacity = Convert.ToSingle(opacityComboBox.Items[opacityComboBox.SelectedIndex] as string) / 100f;
            _settings.ShowJoinsParts = showPartsJoinsCheckBox.Checked;
            _settings.ParseTwitchEmotes = parseStandardMacrosCheckBox.Checked;
            
            // Broadcaster
            _settings.BroadcasterNicknameFontColor = broadcasterNicknameColorViewLabel.BackColor;
            _settings.DifferentBroadcasterNicknameOutline = broadcasterNicknameOutlineColorCheckBox.Checked;
            _settings.BroadcasterNicknameFontOutlineColor = broadcasterNicknameOutlineColorViewLabel.BackColor;
            _settings.BroadcasterMessageFontColor = broadcasterMessageColorViewLabel.BackColor;
            _settings.DifferentBroadcasterMessageOutline = broadcasterMessageOutlineColorCheckBox.Checked;
            _settings.BroadcasterMessageFontOutlineColor = broadcasterMessageOutlineColorViewLabel.BackColor;

            // Moderator
            _settings.ModeratorNicknameFontColor = moderatorNicknameColorViewLabel.BackColor;
            _settings.DifferentModeratorNicknameOutline = moderatorNicknameOutlineColorCheckBox.Checked;
            _settings.ModeratorNicknameFontOutlineColor = moderatorNicknameOutlineColorViewLabel.BackColor;
            _settings.ModeratorMessageFontColor = moderatorMessageColorViewLabel.BackColor;
            _settings.DifferentModeratorMessageOutline = moderatorMessageOutlineColorCheckBox.Checked;
            _settings.ModeratorMessageFontOutlineColor = moderatorMessageOutlineColorViewLabel.BackColor;

            // Friend
            _settings.FriendNicknameFontColor = friendNicknameColorViewLabel.BackColor;
            _settings.DifferentFriendNicknameOutline = friendNicknameOutlineColorCheckBox.Checked;
            _settings.FriendNicknameFontOutlineColor = friendNicknameOutlineColorViewLabel.BackColor;
            _settings.FriendMessageFontColor = friendMessageColorViewLabel.BackColor;
            _settings.DifferentFriendMessageOutline = friendMessageOutlineColorCheckBox.Checked;
            _settings.FriendMessageFontOutlineColor = friendMessageOutlineColorViewLabel.BackColor;

            // Foe
            _settings.FoeNicknameFontColor = foeNicknameColorViewLabel.BackColor;
            _settings.DifferentFoeNicknameOutline = foeNicknameOutlineColorCheckBox.Checked;
            _settings.FoeNicknameFontOutlineColor = foeNicknameOutlineColorViewLabel.BackColor;
            _settings.FoeMessageFontColor = foeMessageColorViewLabel.BackColor;
            _settings.DifferentFoeMessageOutline = foeMessageOutlineColorCheckBox.Checked;
            _settings.FoeMessageFontOutlineColor = foeMessageOutlineColorViewLabel.BackColor;
            
            // User
            _settings.UserNicknameFontColor = userNicknameColorViewLabel.BackColor;
            _settings.DifferentUserNicknameOutline = userNicknameOutlineColorCheckBox.Checked;
            _settings.UserNicknameFontOutlineColor = userNicknameOutlineColorViewLabel.BackColor;
            _settings.UserMessageFontColor = userMessageColorViewLabel.BackColor;
            _settings.DifferentUserMessageOutline = userMessageOutlineColorCheckBox.Checked;
            _settings.UserMessageFontOutlineColor = userMessageOutlineColorViewLabel.BackColor;

            // Lists
            if (moderatorListView.Items.Count > 0)
                _settings.ModeratorList = moderatorListView.Items.Cast<ListViewItem>().Select(i => i.Text).ToList();
            else
                _settings.ModeratorList = new List<string>();

            if (friendListView.Items.Count > 0)
                _settings.FriendList = friendListView.Items.Cast<ListViewItem>().Select(i => i.Text).ToList();
            else
                _settings.FriendList = new List<string>();

            if (foeListView.Items.Count > 0)
                _settings.FoeList = foeListView.Items.Cast<ListViewItem>().Select(i => i.Text).ToList();
            else
                _settings.FoeList = new List<string>();

            if (ignoreListView.Items.Count > 0)
                _settings.IgnoreList = ignoreListView.Items.Cast<ListViewItem>().Select(i => i.Text).ToList();
            else
                _settings.IgnoreList = new List<string>();
        }

        private void UpdateConfigByLoginPreset()
        {
            switch (presetComboBox.SelectedIndex)
            {
                case (int)LoginPreset.Twitch:
                    serverTextBox.Text = "irc.twitch.tv";
                    serverTextBox.Enabled = false;
                    portTextBox.Text = "6667";
                    portTextBox.Enabled = false;
                    broadcasterNickNameTextBox.Text = nickNameTextBox.Text;
                    channelNameTextBox.Text = nickNameTextBox.Text;
                    passTextBox.Width = 272;
                    passHelpButton.Visible = true;
                    channelNameTextBox.Width = 272;
                    channelNameHelpButton.Visible = true;
                    break;

                default:
                    serverTextBox.Enabled = true;
                    portTextBox.Enabled = true;
                    passTextBox.Width = passHelpButton.Left - passTextBox.Left + passHelpButton.Width;
                    passHelpButton.Visible = false;
                    channelNameTextBox.Width = channelNameHelpButton.Left - channelNameTextBox.Left + channelNameHelpButton.Width;
                    channelNameHelpButton.Visible = false;
                    break;
            }
        }

        private void UpdateDifferentNamesFromNickName()
        {
            _differentBroadcasterNickName = broadcasterNickNameTextBox.Text != nickNameTextBox.Text;
            _differentChannelName = channelNameTextBox.Text != nickNameTextBox.Text;
        }

        private void UpdateFontDisplay()
        {
            Font font = fontDialog.Font;
            fontTextBox.Text = String.Format("{0}; {1}; {2}", font.FontFamily.Name, font.Size, font.Style);            
        }

        private float TimeTextToSeconds(string timeText)
        {
            if (String.IsNullOrWhiteSpace(timeText))
                throw new ArgumentNullException("timeText");

            int msValue = GetValueFromTextByUnit(timeText, "ms");
            int sValue = GetValueFromTextByUnit(timeText, "s");
            int mValue = GetValueFromTextByUnit(timeText, "min");
            int hValue = GetValueFromTextByUnit(timeText, "h");

            return (float)(new TimeSpan(0, hValue, mValue, sValue, msValue)).TotalSeconds;
        }

        private string _numbers = "0123456789";
        private int GetValueFromTextByUnit(string timeText, string unit)
        {
            int unitStartPosition = timeText.IndexOf(unit);
            while (unitStartPosition > 0)
            {
                if (_numbers.Any(c => c == timeText[unitStartPosition - 1])) // index - 1 must be a number
                {
                    if (unitStartPosition + unit.Length >= timeText.Length) // index + unit length == out of range is good
                        break;

                    if (timeText[unitStartPosition + unit.Length] == ',') // index + unit length == , is also good
                        break;
                }

                if (unitStartPosition + 1 >= timeText.Length) // not found
                {
                    unitStartPosition = -1;
                    break;
                }

                unitStartPosition = timeText.IndexOf(unit, unitStartPosition + 1); // didn't break, search on
            }
            
            if (unitStartPosition > 0) // cant be 0, there would be no value
            {
                string valueString = "";
                for (int i = unitStartPosition - 1; i >= 0; i--)
                {
                    char valueChar = timeText[i];
                    if (_numbers.Any(c => c == valueChar))
                        valueString = valueChar + valueString;
                    else
                        break;
                }

                if (!String.IsNullOrWhiteSpace(valueString))
                    return Convert.ToInt32(valueString);
            }

            return 0;
        }

        private int ToInt(string value)
        {
            try
            {
                return Convert.ToInt32(value);
            }
            catch
            {
                return 0;
            }
        }

        private void testConnectionButton_Click(object sender, EventArgs e)
        {
            if (LoginCredentialsAreValid())
            {
                StopIrcListener();
                StartIrcListener();
            }
            else
                MessageBox.Show(this, "Invalid input.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private void StartIrcListener()
        {
            _ircManager = new IrcManager(new IrcManagerSettings
            {
                HostName = serverTextBox.Text,
                Port = ToInt(portTextBox.Text),
                UserName = nickNameTextBox.Text,
                NickName = nickNameTextBox.Text,
                RealName = nickNameTextBox.Text,
                Password = passTextBox.Text,
                ChannelName = channelNameTextBox.Text,
                BroadcasterNickName = broadcasterNickNameTextBox.Text
            });

            _ircManager.ConnectionEstablished += _ircListener_ConnectionEstablished;
            _ircManager.ConnectionFailed += _ircListener_ConnectionFailed;
            _ircManager.Start();
        }

        private void _ircListener_ConnectionEstablished(object sender, EventArgs e)
        {
            StopIrcListener();
            MessageBox.Show(this, "Connection established.", "Connection test", MessageBoxButtons.OK, MessageBoxIcon.None);
        }

        private void _ircListener_ConnectionFailed(object sender, EventArgs e)
        {
            StopIrcListener();
            MessageBox.Show(this, "Connection failed. Could not connect within 10sec.", "Connection test", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private void StopIrcListener()
        {
            if (_ircManager != null)
            {
                _ircManager.ConnectionEstablished -= _ircListener_ConnectionEstablished;
                _ircManager.ConnectionFailed -= _ircListener_ConnectionFailed;

                if (_ircManager.IsConnected)
                    _ircManager.Stop();

                while (_ircManager.IsConnected)
                    Thread.Sleep(50);

                _ircManager.Dispose();
                _ircManager = null;
            }
        }

        private void fontSelectButton_Click(object sender, EventArgs e)
        {
            try
            {
                fontDialog.FontMustExist = true;
                fontDialog.Font = _settings.Font;
                if (fontDialog.ShowDialog(this) == DialogResult.OK)
                    UpdateFontDisplay();
            }
            catch (Exception ex)
            {
                // OBS crashed two times when I had the font "Moire; Bold; 14,75" selected. I can't reproduce the bug anymore and this "fix" hopefully is a fix :/ ...

                MessageBox.Show(this, String.Format("The selected font just caused an exception. Please consider an other font or try again with other settings (size, weight, etc.). Exception message: {0}", ex.Message), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

                fontDialog.FontMustExist = true;
                fontDialog.Font = new Font("Arial", 12f);
                if (fontDialog.ShowDialog(this) == DialogResult.OK)
                    UpdateFontDisplay();
            }
        }

        private void PickColorButton(object sender, EventArgs e)
        {
            Button button = sender as Button;
            if (button != null)
            {
                Label label = ((Button)sender).Tag as Label;
                if (label != null)
                {
                    colorDialog.Color = label.BackColor;
                    colorDialog.FullOpen = true;
                    if (colorDialog.ShowDialog(this) == DialogResult.OK)
                        label.BackColor = colorDialog.Color;
                }
            }
        }

        private void PickColor(object sender, EventArgs e)
        {
            Label label = sender as Label;
            if (label != null)
            {
                ColorPickerDialog colorPickerDialog = new ColorPickerDialog();
                colorPickerDialog.Color = label.BackColor;
                if (colorPickerDialog.ShowDialog(this) == DialogResult.OK)
                    label.BackColor = colorPickerDialog.Color;
            }

            //Label label = sender as Label;
            //if (label != null)
            //{
            //    colorDialog.Color = label.BackColor;
            //    colorDialog.FullOpen = true;
            //    if (colorDialog.ShowDialog(this) == DialogResult.OK)
            //        label.BackColor = colorDialog.Color;
            //}
        }

        private void UpdateBroadcasterNicknameOutlineControllsState()
        {
            broadcasterNicknameOutlineColorViewLabel.Enabled = broadcasterNicknameOutlineColorCheckBox.Checked;
        }

        private void UpdateBroadcasterMessageOutlineControllsState()
        {
            broadcasterMessageOutlineColorViewLabel.Enabled = broadcasterMessageOutlineColorCheckBox.Checked;
        }

        private void UpdateModeratorNicknameOutlineControllsState()
        {
            moderatorNicknameOutlineColorViewLabel.Enabled = moderatorNicknameOutlineColorCheckBox.Checked;
        }

        private void UpdateModeratorMessageOutlineControllsState()
        {
            moderatorMessageOutlineColorViewLabel.Enabled = moderatorMessageOutlineColorCheckBox.Checked;
        }

        private void UpdateFriendNicknameOutlineControllsState()
        {
            friendNicknameOutlineColorViewLabel.Enabled = friendNicknameOutlineColorCheckBox.Checked;
        }

        private void UpdateFriendMessageOutlineControllsState()
        {
            friendMessageOutlineColorViewLabel.Enabled = friendMessageOutlineColorCheckBox.Checked;
        }

        private void UpdateFoeNicknameOutlineControllsState()
        {
            foeNicknameOutlineColorViewLabel.Enabled = foeNicknameOutlineColorCheckBox.Checked;
        }

        private void UpdateFoeMessageOutlineControllsState()
        {
            foeMessageOutlineColorViewLabel.Enabled = foeMessageOutlineColorCheckBox.Checked;
        }

        private void UpdateUserNicknameOutlineControllsState()
        {
            userNicknameOutlineColorViewLabel.Enabled = userNicknameOutlineColorCheckBox.Checked;
        }

        private void UpdateUserMessageOutlineControllsState()
        {
            userMessageOutlineColorViewLabel.Enabled = userMessageOutlineColorCheckBox.Checked;
        }

        private void UpdateOutlineControllsState()
        {
            bool enabled = outlineCheckBox.Checked;
            outlineThicknessLabel.Enabled = enabled;
            outlineThicknessComboBox.Enabled = enabled;
            outlineColorLabel.Enabled = enabled;
            outlineColorViewLabel.Enabled = enabled;

            broadcasterNicknameOutlineColorCheckBox.Enabled = enabled;
            broadcasterNicknameOutlineColorViewLabel.Enabled = enabled && broadcasterNicknameOutlineColorCheckBox.Checked;
            broadcasterMessageOutlineColorCheckBox.Enabled = enabled;
            broadcasterMessageOutlineColorViewLabel.Enabled = enabled && broadcasterMessageOutlineColorCheckBox.Checked;

            moderatorNicknameOutlineColorCheckBox.Enabled = enabled;
            moderatorNicknameOutlineColorViewLabel.Enabled = enabled && moderatorNicknameOutlineColorCheckBox.Checked;
            moderatorMessageOutlineColorCheckBox.Enabled = enabled;
            moderatorMessageOutlineColorViewLabel.Enabled = enabled && moderatorMessageOutlineColorCheckBox.Checked;

            friendNicknameOutlineColorCheckBox.Enabled = enabled;
            friendNicknameOutlineColorViewLabel.Enabled = enabled && friendNicknameOutlineColorCheckBox.Checked;
            friendMessageOutlineColorCheckBox.Enabled = enabled;
            friendMessageOutlineColorViewLabel.Enabled = enabled && friendMessageOutlineColorCheckBox.Checked;

            foeNicknameOutlineColorCheckBox.Enabled = enabled;
            foeNicknameOutlineColorViewLabel.Enabled = enabled && foeNicknameOutlineColorCheckBox.Checked;
            foeMessageOutlineColorCheckBox.Enabled = enabled;
            foeMessageOutlineColorViewLabel.Enabled = enabled && foeMessageOutlineColorCheckBox.Checked;

            userNicknameOutlineColorCheckBox.Enabled = enabled;
            userNicknameOutlineColorViewLabel.Enabled = enabled && userNicknameOutlineColorCheckBox.Checked;
            userMessageOutlineColorCheckBox.Enabled = enabled;
            userMessageOutlineColorViewLabel.Enabled = enabled && userMessageOutlineColorCheckBox.Checked;
        }

        private void UpdateNicknameColorsControlsState()
        {
            bool enabled = ! twitchColorsCheckBox.Checked;
          
            broadcasterNicknameColorLabel.Enabled = enabled;
            broadcasterNicknameColorViewLabel.Enabled = enabled;

            moderatorNicknameColorLabel.Enabled = enabled;
            moderatorNicknameColorViewLabel.Enabled = enabled;

            friendNicknameColorLabel.Enabled = enabled;
            friendNicknameColorViewLabel.Enabled = enabled;

            foeNicknameColorLabel.Enabled = enabled;
            foeNicknameColorViewLabel.Enabled = enabled;

            userNicknameColorLabel.Enabled = enabled;
            userNicknameColorViewLabel.Enabled = enabled;
        }

        private void UpdateOutlineControllsStateInOrder()
        {
            UpdateBroadcasterNicknameOutlineControllsState();
            UpdateBroadcasterMessageOutlineControllsState();
            UpdateModeratorNicknameOutlineControllsState();
            UpdateModeratorMessageOutlineControllsState();
            UpdateFriendNicknameOutlineControllsState();
            UpdateFriendMessageOutlineControllsState();
            UpdateFoeNicknameOutlineControllsState();
            UpdateFoeMessageOutlineControllsState();
            UpdateUserNicknameOutlineControllsState();
            UpdateUserMessageOutlineControllsState();

            UpdateOutlineControllsState();
        }

        private void outlineCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateOutlineControllsState();
        }

        private void twitchColorsCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateNicknameColorsControlsState();
        }

        private void broadcasterNicknameOutlineColorCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateBroadcasterNicknameOutlineControllsState();
        }

        private void broadcasterMessageOutlineColorCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateBroadcasterMessageOutlineControllsState();
        }

        private void moderatorNicknameOutlineColorCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateModeratorNicknameOutlineControllsState();
        }

        private void moderatorMessageOutlineColorCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateModeratorMessageOutlineControllsState();
        }

        private void friendNicknameOutlineColorCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateFriendNicknameOutlineControllsState();
        }

        private void friendMessageOutlineColorCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateFriendMessageOutlineControllsState();
        }

        private void foeNicknameOutlineColorCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateFoeNicknameOutlineControllsState();
        }

        private void foeMessageOutlineColorCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateFoeMessageOutlineControllsState();
        }

        private void userNicknameOutlineColorCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateUserNicknameOutlineControllsState();
        }

        private void userMessageOutlineColorCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            UpdateUserMessageOutlineControllsState();
        }

        private void TwitchChatConfigurationDialog_Load(object sender, EventArgs e)
        {
            LoadConfig();
        }

        private void oAuthTokenHelpButton_Click(object sender, EventArgs e)
        {
            OAuthInfoDialog oAuthConfigurationDialog = new OAuthInfoDialog();
            oAuthConfigurationDialog.ShowDialog(this);
        }

        private void nickNameFormatHelpButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show(this, "Use {time} to display time and {nick} to display the nick name.", "Format Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void messageFormatHelpButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show(this, "Use {msg} to display the message.", "Format Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void timeFormatHelpButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show(this, "Use \"H\" for 24-hour clock from 0 to 23."
                + "\r\n\"HH\" for 24-hour clock from 00 to 23."
                + "\r\n\"h\" for 12-hour clock from 0 to 12."
                + "\r\n\"hh\" for 12-hour clock from 00 to 12."
                + "\r\n\"m\" for the minute 0 through 59."
                + "\r\n\"mm\" for the minute 00 through 59."
                + "\r\n\"t\" for the first character of the AM/PM designator."
                + "\r\n\"tt\" for the AM/PM designator."
                , "Format Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void tabPage1_Paint(object sender, PaintEventArgs e)
        {
            Image logo = Properties.Resources.ResourceManager.GetObject("DesertKharonIrc") as Image;
            if (logo != null)
            {
                int height = (int)((float)logo.Height / ((float)logo.Width / (float)tabPage1.Width));
                e.Graphics.DrawImage(logo, new Rectangle(0, tabPage1.Height - height, tabPage1.Width, height));
            }
        }

        private void nickNameTextBox_TextChanged(object sender, EventArgs e)
        {
            nickNameTextBox.BackColor = SystemColors.Window;
            if (presetComboBox.SelectedIndex == (int)LoginPreset.Twitch)
            {
                if (!_differentBroadcasterNickName)
                    broadcasterNickNameTextBox.Text = nickNameTextBox.Text;

                if (!_differentChannelName)
                    channelNameTextBox.Text = nickNameTextBox.Text;
            }
        }

        private void presetComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateConfigByLoginPreset();
        }

        private void presetHelpButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show(this, "By selecting anything different from \"Custom\" you will overwrite some of the values you have typed in (mostly server and port). Switching back to \"Custom\" doesn't restore the old values at the moment.", 
                "Preset info", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void broadcasterNickNameTextBox_TextChanged(object sender, EventArgs e)
        {
            broadcasterNickNameTextBox.BackColor = SystemColors.Window;
            _differentBroadcasterNickName = broadcasterNickNameTextBox.Text != nickNameTextBox.Text;
        }

        private void channelNameTextBox_TextChanged(object sender, EventArgs e)
        {
            channelNameTextBox.BackColor = SystemColors.Window;

            if (presetComboBox.SelectedIndex == (int)LoginPreset.Twitch)
            {
                _differentChannelName = channelNameTextBox.Text.ToLower().Trim() != nickNameTextBox.Text.ToLower().Trim();

                int positionInText = channelNameTextBox.SelectionStart;
                channelNameTextBox.Text = channelNameTextBox.Text.ToLower();

                try
                {
                    // Should work fine. I have tested it, but to be sure ... favor awkward behavior over OBS crash :P ...
                    channelNameTextBox.SelectionStart = positionInText; 
                } catch { }
            }
            else
                _differentChannelName = channelNameTextBox.Text != nickNameTextBox.Text;
        }

        private void AddNickNameToListView(ListView listView, TextBox textBox)
        {
            string nickName = textBox.Text;
            if (!String.IsNullOrWhiteSpace(nickName))
            {
                if (!listView.Items.Cast<ListViewItem>().Any(i => i.Text.IsLike(nickName)))
                {
                    listView.Items.Add(new ListViewItem(nickName));
                    textBox.Text = "";
                }
                else
                    MessageBox.Show(this, "The list already contains this name.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
                MessageBox.Show(this, "The nick name has to contain letters.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);

            textBox.Focus();
        }

        private void RemoveSelectedNickNamesFromListView(ListView listView)
        {
            List<ListViewItem> itemsToRemove = listView.Items.Cast<ListViewItem>().Where(i => i.Checked).ToList();
            if (itemsToRemove != null && itemsToRemove.Count > 0)
                itemsToRemove.ForEach(i => listView.Items.Remove(i));
            else
                MessageBox.Show(this, "No nick name selected.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);

            listView.Focus();
        }

        private void addModeratorButton_Click(object sender, EventArgs e)
        {
            AddNickNameToListView(moderatorListView, addModeratorTextBox);
        }

        private void removeModeratorButton_Click(object sender, EventArgs e)
        {
            RemoveSelectedNickNamesFromListView(moderatorListView);
        }

        private void addFriendButton_Click(object sender, EventArgs e)
        {
            AddNickNameToListView(friendListView, addFriendTextBox);
        }

        private void removeFriendButton_Click(object sender, EventArgs e)
        {
            RemoveSelectedNickNamesFromListView(friendListView);
        }

        private void addFoeButton_Click(object sender, EventArgs e)
        {
            AddNickNameToListView(foeListView, addFoeTextBox);
        }

        private void removeFoeButton_Click(object sender, EventArgs e)
        {
            RemoveSelectedNickNamesFromListView(foeListView);
        }

        private void addIgnoredButton_Click(object sender, EventArgs e)
        {
            AddNickNameToListView(ignoreListView, addIgnoredTextBox);
        }

        private void removeIgnoredButton_Click(object sender, EventArgs e)
        {
            RemoveSelectedNickNamesFromListView(ignoreListView);
        }

        private void discussCodeplexLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Process.Start("http://desertkharonirc.codeplex.com/discussions");
        }

        private void discussObsProjectLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Process.Start("https://obsproject.com/forum/threads/desert-kharon-irc-plugin.12612/");
        }

        private void sourceCodeLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Process.Start("http://desertkharonirc.codeplex.com/SourceControl/list/changesets");
        }

        private void submitIssueCodeplexLinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Process.Start("http://desertkharonirc.codeplex.com/workitem/list/basic");
        }

        private void ConfigurationDialog_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (this.DialogResult == DialogResult.OK)
            {
                if (LoginTabIsValid())
                    SaveConfig();
                else
                {
                    tabControl1.SelectTab(0);
                    e.Cancel = true;

                    MessageBox.Show(this, "Invalid input.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private Color _errorColor = Color.FromArgb(255, 128, 128);
        private bool LoginCredentialsAreValid()
        {
            bool isValid = true;

            if (String.IsNullOrWhiteSpace(nickNameTextBox.Text))
            {
                nickNameTextBox.BackColor = _errorColor;
                isValid = false;
            }

            if (String.IsNullOrWhiteSpace(passTextBox.Text))
            {
                passTextBox.BackColor = _errorColor;
                isValid = false;
            }

            if (String.IsNullOrWhiteSpace(serverTextBox.Text))
            {
                serverTextBox.BackColor = _errorColor;
                isValid = false;
            }

            if (String.IsNullOrWhiteSpace(portTextBox.Text) || !portTextBox.Text.IsInt())
            {
                portTextBox.BackColor = _errorColor;
                isValid = false;
            }

            return isValid;
        }

        private bool LoginTabIsValid()
        {
            bool isValid = LoginCredentialsAreValid();

            if (String.IsNullOrWhiteSpace(broadcasterNickNameTextBox.Text))
            {
                broadcasterNickNameTextBox.BackColor = _errorColor;
                isValid = false;
            }

            if (String.IsNullOrWhiteSpace(channelNameTextBox.Text))
            {
                channelNameTextBox.BackColor = _errorColor;
                isValid = false;
            }

            return isValid;
        }

        private void passTextBox_TextChanged(object sender, EventArgs e)
        {
            passTextBox.BackColor = SystemColors.Window;
        }

        private void serverTextBox_TextChanged(object sender, EventArgs e)
        {
            serverTextBox.BackColor = SystemColors.Window;
        }

        private void portTextBox_TextChanged(object sender, EventArgs e)
        {
            portTextBox.BackColor = SystemColors.Window;
        }

        private void channelNameHelpButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show(this, "Twitch.tv uses lower case nick names for channel naming. If you want to use upper case letters then please select the \"Custom\" preset instead of \"Twitch.tv\".",
                "Channel name info", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        private void updateChacheButton_Click(object sender, EventArgs e)
        {
            TwitchEmoteCacheUpdateDialog dialog = new TwitchEmoteCacheUpdateDialog(DesertKharonIrcPlugin.GetEmoteFolder());
            dialog.ShowDialog(this);
        }

        private void openEmoteFolderButton_Click(object sender, EventArgs e)
        {
            Process.Start(DesertKharonIrcPlugin.GetEmoteFolder());
        }

        private void followupNicknameFormatHelpButton_Click(object sender, EventArgs e)
        {
            MessageBox.Show(this, "Use {time} to display time and {nick} to display the nick name.", "Format Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }
}
